package com.sip;

import java.io.ByteArrayInputStream;
import java.security.NoSuchAlgorithmException;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Random;
import java.util.Vector;

import javax.sdp.Connection;
import javax.sdp.SdpException;
import javax.sdp.Version;
import javax.sip.ClientTransaction;
import javax.sip.Dialog;
import javax.sip.DialogTerminatedEvent;
import javax.sip.IOExceptionEvent;
import javax.sip.InvalidArgumentException;
import javax.sip.ListeningPoint;
import javax.sip.RequestEvent;
import javax.sip.ResponseEvent;
import javax.sip.ServerTransaction;
import javax.sip.SipException;
import javax.sip.SipFactory;
import javax.sip.SipListener;
import javax.sip.SipProvider;
import javax.sip.TimeoutEvent;
import javax.sip.TransactionAlreadyExistsException;
import javax.sip.TransactionDoesNotExistException;
import javax.sip.TransactionTerminatedEvent;
import javax.sip.TransactionUnavailableException;
import javax.sip.address.Address;
import javax.sip.address.AddressFactory;
import javax.sip.address.SipURI;
import javax.sip.address.URI;
import javax.sip.header.AuthorizationHeader;
import javax.sip.header.CSeqHeader;
import javax.sip.header.CallIdHeader;
import javax.sip.header.ContactHeader;
import javax.sip.header.ContentTypeHeader;
import javax.sip.header.ExpiresHeader;
import javax.sip.header.FromHeader;
import javax.sip.header.Header;
import javax.sip.header.HeaderFactory;
import javax.sip.header.MaxForwardsHeader;
import javax.sip.header.ToHeader;
import javax.sip.header.ViaHeader;
import javax.sip.message.MessageFactory;
import javax.sip.message.Request;
import javax.sip.message.Response;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.util.StringUtils;

import com.sip.common.DialogHelper;
import com.sip.common.DigestServerAuthenticationHelper;
import com.sip.common.PtzCmdHelper;
import com.sip.db.entity.ChannelEntity;
import com.sip.db.entity.DeviceEntity;
import com.sip.db.service.ChannelService;
import com.sip.db.service.DeviceService;

import gov.nist.javax.sdp.MediaDescriptionImpl;
import gov.nist.javax.sdp.SessionDescriptionImpl;
import gov.nist.javax.sdp.TimeDescriptionImpl;
import gov.nist.javax.sdp.fields.AttributeField;
import gov.nist.javax.sdp.fields.ConnectionField;
import gov.nist.javax.sdp.fields.MediaField;
import gov.nist.javax.sdp.fields.OriginField;
import gov.nist.javax.sdp.fields.ProtoVersionField;
import gov.nist.javax.sdp.fields.SessionNameField;
import gov.nist.javax.sdp.fields.TimeField;
import gov.nist.javax.sip.SipStackImpl;
import gov.nist.javax.sip.address.AddressImpl;
import gov.nist.javax.sip.address.SipUri;
import gov.nist.javax.sip.header.Expires;

public class SipLayer implements SipListener{
	
	String sipIp;
	Integer sipPort;
	String sipDeviceId;
	String sipDomain;
	String sipPassword;
	String mediaIp;
	Integer mediaPort;

	private SipStackImpl sipStack;
	private AddressFactory addressFactory;
	private HeaderFactory headerFactory;
	private MessageFactory messageFactory;
	private SipProvider tcpSipProvider;
	private SipProvider udpSipProvider;
	
	private DeviceService deviceService;
	private ChannelService channelService;

	/************************************************************/

	public SipLayer(ChannelService channelService,DeviceService deviceService,String sipIp, Integer sipPort, String sipDeviceId, String sipDomain, String sipPassword,String mediaIp,Integer mediaPort) {
		this.sipIp = sipIp;
		this.sipPort = sipPort;
		this.sipDeviceId = sipDeviceId;
		this.sipDomain = sipDomain;
		this.sipPassword = sipPassword;
		this.mediaIp = mediaIp;
		this.mediaPort = mediaPort;
		this.deviceService = deviceService;
		this.channelService = channelService;
	}

	public boolean startServer() {
		return initSip();
	}

	private boolean initSip() {
		try {
			
			SipFactory sipFactory = SipFactory.getInstance();
			
			Properties properties = new Properties();
			properties.setProperty("javax.sip.STACK_NAME", "GB28181_SIP");
			properties.setProperty("javax.sip.IP_ADDRESS", sipIp);
			
			sipStack = (SipStackImpl) sipFactory.createSipStack(properties);

			headerFactory = sipFactory.createHeaderFactory();
			addressFactory = sipFactory.createAddressFactory();
			messageFactory = sipFactory.createMessageFactory();
			
			// 同时监听UDP和TCP
			try {
				ListeningPoint tcpListeningPoint = sipStack.createListeningPoint(sipIp, sipPort, "TCP");

				ListeningPoint udpListeningPoint = sipStack.createListeningPoint(sipIp, sipPort, "UDP");

				tcpSipProvider = sipStack.createSipProvider(tcpListeningPoint);
				tcpSipProvider.addSipListener(this);

				udpSipProvider = sipStack.createSipProvider(udpListeningPoint);
				udpSipProvider.addSipListener(this);
			} catch (Exception e) {
				e.printStackTrace();
				return false;
			}
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
		return true;
	}
	
	@Override
	public void processRequest(RequestEvent requestEvent) {
		Request request = requestEvent.getRequest();
		String method = request.getMethod();
		if (method.equalsIgnoreCase(Request.REGISTER)) {
			processRequestRegister(requestEvent);
		}
		if (method.equalsIgnoreCase(Request.MESSAGE) && new String(request.getRawContent()).contains("<CmdType>Keepalive</CmdType>")) {
			processRequestKeepAlive(requestEvent);
		}
		if (method.equalsIgnoreCase(Request.MESSAGE) && new String(request.getRawContent()).contains("<CmdType>Catalog</CmdType>")) {
			processRequestCatalogList(requestEvent);
		}
		if (method.equalsIgnoreCase(Request.MESSAGE) && new String(request.getRawContent()).contains("<CmdType>RecordInfo</CmdType>")) {
			processRequestReplayList(requestEvent);
		}
	}

	@Override
	public void processResponse(ResponseEvent responseEvent) {
		Response response = responseEvent.getResponse();
		CSeqHeader cseqHeader = (CSeqHeader) response.getHeader(CSeqHeader.NAME);
		String method = cseqHeader.getMethod();
		int status = response.getStatusCode();
		if ((status >= 200) && (status < 300)) { // Success!
			if(Request.INVITE.equals(method)){
				processResponseAck(responseEvent);
			}
		}
		//trying不会回复
		if(status == Response.TRYING){

		}
		
	}

	@Override
	public void processTimeout(TimeoutEvent timeoutEvent) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void processIOException(IOExceptionEvent exceptionEvent) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void processTransactionTerminated(TransactionTerminatedEvent transactionTerminatedEvent) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void processDialogTerminated(DialogTerminatedEvent dialogTerminatedEvent) {
		// TODO Auto-generated method stub
		
	}
	/************************************************************/
	/***
	 * 收到注册请求 处理
	 */
	public void processRequestRegister(RequestEvent evt) {
		try {
			Request request = evt.getRequest();
			ServerTransaction serverTransaction = evt.getServerTransaction();
			// 判断TCP还是UDP
			boolean isTcp = false;
			ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
			String transport = reqViaHeader.getTransport();
			if (transport.equals("TCP")) {
				isTcp = true;
			}
			/**
			 * if(protocol.equals("UDP")) { isTcp = false; }
			 **/
			//
			if (serverTransaction == null) {
				if (isTcp) {
					serverTransaction = tcpSipProvider.getNewServerTransaction(request);
				} else {
					serverTransaction = udpSipProvider.getNewServerTransaction(request);
				}
			}
			//
			Response response = null;
			boolean passwordCorrect = false;
			boolean isRegisterSuceess = false;
			DeviceEntity device = null;
			Header header = request.getHeader(AuthorizationHeader.NAME);
			// 携带授权头
			// 校验密码是否正确
			if (header != null) {
				passwordCorrect = new DigestServerAuthenticationHelper().doAuthenticatePlainTextPassword(request,sipPassword);
				if (!passwordCorrect) {
					System.out.println("密码错误");
					return;
				}
			}
			
			if (header == null || !passwordCorrect) {
				// 未携带授权头或者密码错误 回复401
				response = messageFactory.createResponse(Response.UNAUTHORIZED, request);
				new DigestServerAuthenticationHelper().generateChallenge(headerFactory, response, sipDomain);
			} else if (header != null && passwordCorrect) {
				// 携带授权头并且密码正确
				response = messageFactory.createResponse(Response.OK, request);
				// 添加date头
				response.addHeader(headerFactory.createDateHeader(Calendar.getInstance(Locale.ENGLISH)));
				ExpiresHeader expiresHeader = (ExpiresHeader) request.getHeader(Expires.NAME);
				// 添加Contact头
				response.addHeader(request.getHeader(ContactHeader.NAME));
				// 添加Expires头
				response.addHeader(request.getExpires());
				
				if (expiresHeader != null && expiresHeader.getExpires() == 0) {
					// 注销成功
				} else {
					// 注册成功
					isRegisterSuceess = true;
					// 1.获取到通信地址等信息，保存到Redis
					FromHeader fromHeader = (FromHeader) request.getHeader(FromHeader.NAME);
					ViaHeader viaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
					String received = viaHeader.getReceived();
					int rPort = viaHeader.getRPort();
					// 本地模拟设备 received 为空 rPort 为 -1
					// 解析本地地址替代
					if (StringUtils.isEmpty(received) || rPort == -1) {
						received = viaHeader.getHost();
						rPort = viaHeader.getPort();
					}
					//
					AddressImpl address = (AddressImpl) fromHeader.getAddress();
					SipUri uri = (SipUri) address.getURI();
					String deviceId = uri.getUser();
					device = new DeviceEntity();
					device.setDeviceId(deviceId);
					device.setIp(received);
					device.setPort(rPort);
					device.setAddress(received.concat(":").concat(String.valueOf(rPort)));
					device.setTransport(isTcp ? "TCP" : "UDP");
					device.setHeartTime(new Timestamp(new Date().getTime()));
				}
			}
			serverTransaction.sendResponse(response);
			// 注册成功
			// 保存到db
			// 下发catelog查询目录
			if (isRegisterSuceess && device != null) {
				DeviceEntity findOneDeviceParam = new DeviceEntity();
				findOneDeviceParam.setDeviceId(device.getDeviceId());
				DeviceEntity retDevice = deviceService.findOne(findOneDeviceParam);
				if(retDevice==null) {
					deviceService.saveOrUpdate(device);
				}
				requestCatalogList(device.getDeviceId());
			}
		} catch (SipException | InvalidArgumentException | NoSuchAlgorithmException | ParseException e) {
			e.printStackTrace();
		}
	}
	/************************************************************/
	/***
	 * 收到keepalive请求 处理
	 * @param evt
	 */
	public void processRequestKeepAlive(RequestEvent evt){
		try {
			Request request = evt.getRequest();
			Response response = messageFactory.createResponse(Response.OK,request);
			ServerTransaction serverTransaction = evt.getServerTransaction();
			if(serverTransaction == null){
				ViaHeader viaHeader = (ViaHeader)request.getHeader(ViaHeader.NAME);
				String transport = viaHeader.getTransport();
				if(transport.equals("TCP")) {
					serverTransaction = tcpSipProvider.getNewServerTransaction(request);
				}
				if(transport.equals("UDP")) {
					serverTransaction = udpSipProvider.getNewServerTransaction(request);
				}
				
				SAXReader reader = new SAXReader();
				Document xml = reader.read(new ByteArrayInputStream(request.getRawContent()));
				// reader.setEncoding("GB2312");
				Element rootElement = xml.getRootElement();
				Element deviceIdElement = rootElement.element("DeviceID");
				//更新时间
				DeviceEntity findOneParam = new DeviceEntity();
				findOneParam.setDeviceId(deviceIdElement.getText());
				DeviceEntity retDevice = deviceService.findOne(findOneParam);
				if(retDevice!=null) {
					retDevice.setHeartTime(new Timestamp(new Date().getTime()));
					deviceService.saveOrUpdate(retDevice);
				}
			}
			serverTransaction.sendResponse(response);
		} catch (ParseException | SipException | InvalidArgumentException | DocumentException e) {
			e.printStackTrace();
		}
	}
	/************************************************************/
	/***
	 * 请求目录列表
	 * @param device
	 */
	private void requestCatalogList(String deviceId) {
		try {
			DeviceEntity findOneParam = new DeviceEntity();
			findOneParam.setDeviceId(deviceId);
			DeviceEntity device = deviceService.findOne(findOneParam);
			if(device==null) {
				return;
			}
			//sipuri
			SipURI requestURI = addressFactory.createSipURI(deviceId, device.getAddress());
			//via
			ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
			ViaHeader viaHeader = headerFactory.createViaHeader(sipIp, sipPort, device.getTransport(), "ViaCatalogBranch");
			viaHeaders.add(viaHeader);
			//from
			SipURI fromSipURI = addressFactory.createSipURI(sipDeviceId,sipIp+":"+sipPort);
			Address fromAddress = addressFactory.createAddress(fromSipURI);
			FromHeader fromHeader = headerFactory.createFromHeader(fromAddress,"FromCatalogTag");
			//to
			SipURI toSipURI = addressFactory.createSipURI(deviceId,device.getAddress()); 
			Address toAddress = addressFactory.createAddress(toSipURI);
			ToHeader toHeader = headerFactory.createToHeader(toAddress,"ToCatalogTag");
			//callid
			CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
					: udpSipProvider.getNewCallId();
			//Forwards
			MaxForwardsHeader maxForwards = headerFactory.createMaxForwardsHeader(70);
			//ceq
			CSeqHeader cSeqHeader = headerFactory.createCSeqHeader(1L, Request.MESSAGE);
			  
			Request request = messageFactory.createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
			
			StringBuffer catalogXml = new StringBuffer(200);
			catalogXml.append("<?xml version=\"1.0\" encoding=\"GB2312\"?>\n");
			catalogXml.append("<Query>\n");
			catalogXml.append("<CmdType>Catalog</CmdType>\n");
			catalogXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\n");
			catalogXml.append("<DeviceID>" + deviceId + "</DeviceID>\n");
			catalogXml.append("</Query>\n");
			
			//
			ContentTypeHeader contentTypeHeader = headerFactory.createContentTypeHeader("Application", "MANSCDP+xml");
			
			request.setContent(catalogXml, contentTypeHeader);
			
			if(device.getTransport().equals("TCP")) {
				tcpSipProvider.sendRequest(request);
			}
			if(device.getTransport().equals("UDP")) {
				udpSipProvider.sendRequest(request);
			}
			
		} catch (SipException | ParseException | InvalidArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
	/************************************************************/
	/***
	 * 收到catalog目录列表请求 处理
	 * @param evt
	 */
	public void processRequestCatalogList(RequestEvent evt) {
		try {
			Request request = evt.getRequest();
			SAXReader reader = new SAXReader();
			// reader.setEncoding("GB2312");
			Document xml = reader.read(new ByteArrayInputStream(request.getRawContent()));
			Element rootElement = xml.getRootElement();
			Element deviceIdElement = rootElement.element("DeviceID");
			String deviceId = deviceIdElement.getText().toString();
			DeviceEntity findOneDeviceParam = new DeviceEntity();
			findOneDeviceParam.setDeviceId(deviceId);
			DeviceEntity device = deviceService.findOne(findOneDeviceParam);
			if(device == null) {
				return;
			}
			Element deviceListElement = rootElement.element("DeviceList");
			if (deviceListElement == null) {
				return;
			}
			Iterator<Element> deviceListIterator = deviceListElement.elementIterator();
			if (deviceListIterator != null) {
				// 遍历DeviceList
				while (deviceListIterator.hasNext()) {
					Element itemDevice = deviceListIterator.next();
					String channelId = itemDevice.element("DeviceID").getText().toString();
					String channelName = itemDevice.element("Name").getText().toString();
					String status = itemDevice.element("Status").getText().toString();
					//查询channel是否存在
					ChannelEntity findOneChannelParam = new ChannelEntity();
					findOneChannelParam.setDeviceId(deviceId);
					findOneChannelParam.setChannelId(channelId);
					ChannelEntity retChannel = channelService.findOne(findOneChannelParam);
					if(retChannel == null) {
						ChannelEntity channel = new ChannelEntity();
						channel.setChannelId(channelId);
						channel.setDeviceId(deviceId);
						channel.setName(channelName);
						channel.setStatus(status);
						channelService.saveOrUpdate(channel);
					}
				}
				
			}
		} catch (DocumentException e) {
			e.printStackTrace();
		}
	}
	/************************************************************/
	/***
	 * 请求直播视频流
	 * @param device
	 */
	public void requestInvite(String deviceId,String channelId){
		try {
			DeviceEntity findOneDeviceParam = new DeviceEntity();
			findOneDeviceParam.setDeviceId(deviceId);
			DeviceEntity device = deviceService.findOne(findOneDeviceParam);
			if(device==null) {
				return;
			}
			ChannelEntity findOneChannelParam = new ChannelEntity();
			findOneChannelParam.setDeviceId(deviceId);
			findOneChannelParam.setChannelId(channelId);
			ChannelEntity channel = channelService.findOne(findOneChannelParam);
			if(channel==null) {
				return;
			}
			//请求行
			SipURI requestLine = addressFactory.createSipURI(channelId, device.getAddress());
			//via
			ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
			ViaHeader viaHeader = headerFactory.createViaHeader(sipIp, sipPort, device.getTransport(), null);
			viaHeader.setRPort();
			viaHeaders.add(viaHeader);
			//from
			SipURI fromSipURI = addressFactory.createSipURI(sipDeviceId,sipIp+":"+sipPort);
			Address fromAddress = addressFactory.createAddress(fromSipURI);
			FromHeader fromHeader = headerFactory.createFromHeader(fromAddress, deviceId+"_"+channelId+"_live"); //必须要有标记，否则无法创建会话，无法回应ack
			//to
			SipURI toSipURI = addressFactory.createSipURI(channelId,device.getAddress()); 
			Address toAddress = addressFactory.createAddress(toSipURI);
			ToHeader toHeader = headerFactory.createToHeader(toAddress,null);

			//callid
			CallIdHeader callIdHeader = null;
			if(device.getTransport().equals("TCP")) {
				callIdHeader = tcpSipProvider.getNewCallId();
			}
			if(device.getTransport().equals("UDP")) {
				callIdHeader = udpSipProvider.getNewCallId();
			}
			//Forwards
			MaxForwardsHeader maxForwards = headerFactory.createMaxForwardsHeader(70);
			//ceq
			CSeqHeader cSeqHeader = headerFactory.createCSeqHeader(1L, Request.INVITE);
			Request request = messageFactory.createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
			ContentTypeHeader contentTypeHeader = headerFactory.createContentTypeHeader("Application", "SDP");
			/***
			 * ssrc
			 * 第1位 0-直播 1-回放
			 * 第2-6位 取20位sip域的4-8位
			 * 第7-10位 取channelId后3位
			 */
			String ssrc = "0"+deviceId.substring(3,9)+channelId.substring(channelId.length()-3,channelId.length());
			//String ssrc = "02000"+deviceId.substring(deviceId.length()-4,deviceId.length());
			StringBuffer content = new StringBuffer(200);
	        content.append("v=0\r\n");
	        content.append("o="+channelId+" 0 0 IN IP4 "+sipIp+"\r\n");
	        content.append("s=Play\r\n");
	        content.append("c=IN IP4 "+mediaIp+"\r\n");
	        content.append("t=0 0\r\n");
	        if(device.getTransport().equals("TCP")) {
	        	content.append("m=video "+mediaPort+" TCP/RTP/AVP 96 98 97\r\n");
			}
	        if(device.getTransport().equals("UDP")) {
	        	content.append("m=video "+mediaPort+" RTP/AVP 96 98 97\r\n");
			}
	        content.append("a=sendrecv\r\n");
	        content.append("a=rtpmap:96 PS/90000\r\n");
	        content.append("a=rtpmap:98 H264/90000\r\n");
	        content.append("a=rtpmap:97 MPEG4/90000\r\n");
	        if(device.getTransport().equals("TCP")){
	             content.append("a=setup:passive\r\n");
	             content.append("a=connection:new\r\n");
	        }
	        content.append("y="+ssrc+"\r\n");//ssrc
	        
	        request.setContent(content.toString(), contentTypeHeader);
	        
 			if(device.getTransport().equals("TCP")) {
				tcpSipProvider.sendRequest(request);
			}
			if(device.getTransport().equals("UDP")) {
				udpSipProvider.sendRequest(request);
			}
		} catch ( SipException | ParseException | InvalidArgumentException e) {
			e.printStackTrace();
		} 
	}
	/************************************************************/
	/***
	 * ack
	 * @param evt
	 */
	public void processResponseAck(ResponseEvent evt) {
		try {
			FromHeader form =  (FromHeader) evt.getResponse().getHeader(FromHeader.NAME);
			Dialog dialog = evt.getDialog();
			DialogHelper.dialogMap.put(form.getTag(), dialog);
			Request reqAck =dialog.createAck(1L);
			dialog.sendAck(reqAck);
		} catch (InvalidArgumentException | SipException e) {
			e.printStackTrace();
		}
	}
	/************************************************************/
	/****
	 * 请求回放列表
	 * @param device
	 */
	public void requestReplayList(String deviceId,String channelId,String startTime,String endTime) {
		try {
			DeviceEntity findOneDeviceParam = new DeviceEntity();
			findOneDeviceParam.setDeviceId(deviceId);
			DeviceEntity device = deviceService.findOne(findOneDeviceParam);
			if(device==null) {
				return;
			}
			ChannelEntity findOneChannelParam = new ChannelEntity();
			findOneChannelParam.setDeviceId(deviceId);
			findOneChannelParam.setChannelId(channelId);
			ChannelEntity channel = channelService.findOne(findOneChannelParam);
			if(channel==null) {
				return;
			}
			//sipuri
			SipURI requestURI = addressFactory.createSipURI(deviceId, device.getAddress());
			//via
			ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
			ViaHeader viaHeader = headerFactory.createViaHeader(sipIp, sipPort, device.getTransport(), "ViaCatalogBranch");
			viaHeaders.add(viaHeader);
			//from
			SipURI fromSipURI = addressFactory.createSipURI(sipDeviceId,sipIp+":"+sipPort);
			Address fromAddress = addressFactory.createAddress(fromSipURI);
			FromHeader fromHeader = headerFactory.createFromHeader(fromAddress,"FromReplayListTag");
			//to
			SipURI toSipURI = addressFactory.createSipURI(channelId,device.getAddress()); 
			Address toAddress = addressFactory.createAddress(toSipURI);
			ToHeader toHeader = headerFactory.createToHeader(toAddress,"ToReplayListTag");
			//callid
			CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
					: udpSipProvider.getNewCallId();
			//Forwards
			MaxForwardsHeader maxForwards = headerFactory.createMaxForwardsHeader(70);
			//ceq
			CSeqHeader cSeqHeader = headerFactory.createCSeqHeader(1L, Request.MESSAGE);
			  
			Request request = messageFactory.createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
			
			SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			SimpleDateFormat dft = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
			
		    String nStartTime = dft.format(df.parse(startTime));

		    String nEndTime = dft.format(df.parse(endTime));
		    
			StringBuffer replayXml = new StringBuffer(200);
			replayXml.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
			replayXml.append("<Query>\n");
			replayXml.append("<CmdType>RecordInfo</CmdType>\n");
			replayXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\n");
			replayXml.append("<DeviceID>" + channelId + "</DeviceID>\n");
			replayXml.append("<StartTime>" + nStartTime + "</StartTime>\n");
			replayXml.append("<EndTime>" + nEndTime + "</EndTime>\n");
			replayXml.append("<Type>" + "all" + "</Type>\n");
			replayXml.append("</Query>\n");
			
			//
			ContentTypeHeader contentTypeHeader = headerFactory.createContentTypeHeader("Application", "MANSCDP+xml");
			
			request.setContent(replayXml, contentTypeHeader);
			
			if(device.getTransport().equals("TCP")) {
				tcpSipProvider.sendRequest(request);
			}
			if(device.getTransport().equals("UDP")) {
				udpSipProvider.sendRequest(request);
			}
		} catch (SipException | ParseException | InvalidArgumentException e) {
			e.printStackTrace();
		}
	}
	/************************************************************/
	/***
	 * 接收回放列表
	 * @param evt
	 */
	public void processRequestReplayList(RequestEvent evt) {
		try {
			FromHeader from =  (FromHeader) evt.getRequest().getHeader(FromHeader.NAME);
			Address fromAddr = from.getAddress();
			SipUri fromSip = (SipUri) fromAddr.getURI();
			String deviceId = fromSip.getUser();
			
			Request request = evt.getRequest();
			SAXReader reader = new SAXReader();
			// reader.setEncoding("GB2312");
			Document xml = reader.read(new ByteArrayInputStream(request.getRawContent()));
			Element rootElement = xml.getRootElement();
			Element recordListElement = rootElement.element("RecordList");
			Iterator<Element> recordListIterator = recordListElement.elementIterator();
			// 遍历DeviceList
			while (recordListIterator.hasNext()) {
				Element itemDevice = recordListIterator.next();
				Element channelDeviceElement = itemDevice.element("DeviceID");
				if (channelDeviceElement == null) {
					continue;
				}
				String channelId = channelDeviceElement.getText().toString();
				
				//Element nameElement = itemDevice.element("Name");
				//String name = nameElement != null ? nameElement.getText().toString() : "";
				
				SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
				SimpleDateFormat dft = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
				
				Element startTimeElement = itemDevice.element("StartTime");
				String startTime = startTimeElement != null ? startTimeElement.getText().toString() : "";
			    String nStartTime = df.format(dft.parse(startTime));
				
				Element endTimeElement = itemDevice.element("EndTime");
				String endTime = endTimeElement != null ? endTimeElement.getText().toString() : "";
				String nEndTime = df.format(dft.parse(endTime));
			}
			
		} catch (DocumentException | ParseException e) {
			e.printStackTrace();
		}
	}
	/************************************************************/
	/***
	 * 请求回放视频流
	 * @param device
	 */
	public void requestReplay(String deviceId,String channelId,String startTime,String endTime){
		try {
			DeviceEntity findOneDeviceParam = new DeviceEntity();
			findOneDeviceParam.setDeviceId(deviceId);
			DeviceEntity device = deviceService.findOne(findOneDeviceParam);
			if(device==null) {
				return;
			}
			ChannelEntity findOneChannelParam = new ChannelEntity();
			findOneChannelParam.setDeviceId(deviceId);
			findOneChannelParam.setChannelId(channelId);
			ChannelEntity channel = channelService.findOne(findOneChannelParam);
			if(channel==null) {
				return;
			}
			//请求行
			SipURI requestLine = addressFactory.createSipURI(channelId, device.getAddress());
			//via
			ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
			ViaHeader viaHeader = headerFactory.createViaHeader(sipIp, sipPort, device.getTransport(), null);
			viaHeader.setRPort();
			viaHeaders.add(viaHeader);
			//from
			SipURI fromSipURI = addressFactory.createSipURI(sipDeviceId,sipIp+":"+sipPort);
			Address fromAddress = addressFactory.createAddress(fromSipURI);
			FromHeader fromHeader = headerFactory.createFromHeader(fromAddress, deviceId+"_"+channelId+"_replay"); //必须要有标记，否则无法创建会话，无法回应ack
			//to
			SipURI toSipURI = addressFactory.createSipURI(channelId,device.getAddress()); 
			Address toAddress = addressFactory.createAddress(toSipURI);
			ToHeader toHeader = headerFactory.createToHeader(toAddress,null);

			//callid
			CallIdHeader callIdHeader = null;
			if(device.getTransport().equals("TCP")) {
				callIdHeader = tcpSipProvider.getNewCallId();
			}
			if(device.getTransport().equals("UDP")) {
				callIdHeader = udpSipProvider.getNewCallId();
			}
			//Forwards
			MaxForwardsHeader maxForwards = headerFactory.createMaxForwardsHeader(70);
			//ceq
			CSeqHeader cSeqHeader = headerFactory.createCSeqHeader(1L, Request.INVITE);
			Request request = messageFactory.createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
			ContentTypeHeader contentTypeHeader = headerFactory.createContentTypeHeader("application", "sdp");
			
			Long nStratTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(startTime).getTime() / 1000;
			Long nEndTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(endTime).getTime() / 1000;
			/***
			 * ssrc
			 * 第1位 0-直播 1-回放
			 * 第2-6位 取20位sip域的4-8位
			 * 第7-10位 取channelId后3位
			 */
			String ssrc = "1"+deviceId.substring(3,9)+channelId.substring(channelId.length()-3,channelId.length());
			//String ssrc = "02000"+deviceId.substring(deviceId.length()-4,deviceId.length());
			StringBuffer content = new StringBuffer(200);
	        content.append("v=0\r\n");
	        content.append("o="+channelId+" 0 0 IN IP4 "+sipIp+"\r\n");
	        content.append("s=Playback\r\n");
	        //content.append("u="+channelId+":0\r\n");
	        content.append("c=IN IP4 "+mediaIp+"\r\n");
	        content.append("t="+nStratTime+" "+nEndTime+"\r\n");
	        //content.append("t=1578956400 1586800800\r\n");
	        if(device.getTransport().equals("TCP")) {
	        	content.append("m=video "+mediaPort+" TCP/RTP/AVP 96 98 97\r\n");
			}
	        if(device.getTransport().equals("UDP")) {
	        	content.append("m=video "+mediaPort+" RTP/AVP 96 98 97\r\n");
			}
	        content.append("a=recvonly\r\n");
	        content.append("a=rtpmap:96 PS/90000\r\n");
	        content.append("a=rtpmap:98 H264/90000\r\n");
	        content.append("a=rtpmap:97 MPEG4/90000\r\n");
	        if(device.getTransport().equals("TCP")){
	             content.append("a=setup:passive\r\n");
	             content.append("a=connection:new\r\n");
	        }
	        content.append("y="+ssrc+"\r\n");//ssrc
	        request.setContent(content.toString(), contentTypeHeader);
 			if(device.getTransport().equals("TCP")) {
				tcpSipProvider.sendRequest(request);
			}
			if(device.getTransport().equals("UDP")) {
				udpSipProvider.sendRequest(request);
			}
		} catch ( SipException | ParseException | InvalidArgumentException e) {
			e.printStackTrace();
		} 
	}
	/************************************************************/
	/***
	 * 停止视频推流
	 * method：live replay
	 */
	public void requestBye(String deviceId,String channelId,String method) {
		try {
			DeviceEntity findOneDeviceParam = new DeviceEntity();
			findOneDeviceParam.setDeviceId(deviceId);
			DeviceEntity device = deviceService.findOne(findOneDeviceParam);
			if(device==null) {
				return;
			}
			ChannelEntity findOneChannelParam = new ChannelEntity();
			findOneChannelParam.setDeviceId(deviceId);
			findOneChannelParam.setChannelId(channelId);
			ChannelEntity channel = channelService.findOne(findOneChannelParam);
			if(channel==null) {
				return;
			}
			Dialog d = DialogHelper.dialogMap.get(deviceId+"_"+channelId+"_"+method);
			if(d!=null) {
				DialogHelper.dialogMap.remove(deviceId+"_"+channelId+"_"+method);
			}
			Request byeRequest = d.createRequest(Request.BYE);
			ClientTransaction clientTransaction = null;
			if(device.getTransport().equals("TCP")) {
				clientTransaction = tcpSipProvider.getNewClientTransaction(byeRequest);
			}
			if(device.getTransport().equals("UDP")) {
				clientTransaction = udpSipProvider.getNewClientTransaction(byeRequest);
			}
			d.sendRequest(clientTransaction);
		} catch (SipException e) {
			e.printStackTrace();
		}
	}
	/************************************************************/
	/***
	 * 云台控制
	 */
	public void requestPtz(String deviceId,String channelId,int leftRight, int upDown, int inOut, int moveSpeed, int zoomSpeed) {
		try {
			DeviceEntity findOneDeviceParam = new DeviceEntity();
			findOneDeviceParam.setDeviceId(deviceId);
			DeviceEntity device = deviceService.findOne(findOneDeviceParam);
			if(device==null) {
				return;
			}
			ChannelEntity findOneChannelParam = new ChannelEntity();
			findOneChannelParam.setDeviceId(deviceId);
			findOneChannelParam.setChannelId(channelId);
			ChannelEntity channel = channelService.findOne(findOneChannelParam);
			if(channel==null) {
				return;
			}
			//sipuri
			SipURI requestURI = addressFactory.createSipURI(deviceId, device.getAddress());
			//via
			ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
			ViaHeader viaHeader = headerFactory.createViaHeader(sipIp, sipPort, device.getTransport(), "ViaPtzBranch");
			viaHeaders.add(viaHeader);
			//from
			SipURI fromSipURI = addressFactory.createSipURI(sipDeviceId,sipIp+":"+sipPort);
			Address fromAddress = addressFactory.createAddress(fromSipURI);
			FromHeader fromHeader = headerFactory.createFromHeader(fromAddress,"FromPtzTag");
			//to
			SipURI toSipURI = addressFactory.createSipURI(deviceId,device.getAddress()); 
			Address toAddress = addressFactory.createAddress(toSipURI);
			ToHeader toHeader = headerFactory.createToHeader(toAddress,"ToPtzTag");
			//callid
			CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
					: udpSipProvider.getNewCallId();
			//Forwards
			MaxForwardsHeader maxForwards = headerFactory.createMaxForwardsHeader(70);
			//ceq
			CSeqHeader cSeqHeader = headerFactory.createCSeqHeader(1L, Request.MESSAGE);
			  
			Request request = messageFactory.createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
			
			String ptzXml = "<?xml version=\"1.0\" ?>" +
                    "<Control>" +
                    "<CmdType>DeviceControl</CmdType>" +
                    "<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>" +
                    "<DeviceID>" + channelId + "</DeviceID>" +
                    "<PTZCmd>" + PtzCmdHelper.create(leftRight, upDown, inOut, moveSpeed, zoomSpeed) + "</PTZCmd>" +
                    "<Info>" +
                    //"<ControlPriority>" + ptzInfo.getPriority() + "</ControlPriority>" +
                    "</Info>" +
                    "</Control>";
			//
			ContentTypeHeader contentTypeHeader = headerFactory.createContentTypeHeader("Application", "MANSCDP+xml");
			
			request.setContent(ptzXml, contentTypeHeader);
			
			if(device.getTransport().equals("TCP")) {
				tcpSipProvider.sendRequest(request);
			}
			if(device.getTransport().equals("UDP")) {
				udpSipProvider.sendRequest(request);
			}
			
		} catch (SipException | ParseException | InvalidArgumentException e) {
			e.printStackTrace();
		} 
	}
}
